<?php
namespace models\common;

defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * Tree-Structure Trait
 * 
 * @desc    树型结构数据方法, 提供相关的功能扩展。
 * @desc    注意：
				注意 before_treeStructure_create_data_handler
				注意 需要有的、必选字段：parent_id, name, level, position
				注意 可以冇的、可选字段：sort_order
 * @version 1.0.0 
 * @author  Jason
 * @date	2017年7月3日 下午2:03:53
 * 
 * @desc	本文件来源于 Mod_area.php，抽调相关部分方法。
 * @author	Jason
 * @date	2017年7月3日 下午2:08:05
 */
trait TreeStructure
{
	
	/**
	 * 根据父级ID和module名称查找字数据
	 * 
	 * @param int    $id     id
	 * 
	 * @return array
	 */
	public function get_children_by_id($id)
	{
		if (isset($this->allowed_fields) 
				&& !empty($this->allowed_fields['master']) 
				&& is_array($this->allowed_fields['master']) 
				&& in_array('sort_order', $this->allowed_fields['master'])) {
					
					$this->db
						->order_by('sort_order ASC');
					
		}
		$data = $this->db
						->where('parent_id', $id)
						->get($this->table_mapper['master'])
						->result_array();
		
		return $data;
	}
	
	/**
	 * 根据节点ID获得兄弟节点
	 * 
	 * @param number $id
	 * 
	 * @return array
	 */
	public function get_siblings_by_id($id)
	{
		if ($id == 0) return array();
		$item = $this->select_instance_by_id($id);
		
		$siblings = $this->get_children_by_id($item['parent_id']);
		
		return $siblings;
	}
	
	
	/**
	 * 返回给定分类ID的各级路径，正向分类树
	 * 
	 * @param int $id 给定分类ID
	 * 
	 * @return array 如果分类不存在返回零数组[0]，如果存在返回从顶级到本类的id路径
	 *               eg: 1000 -> array(0, 1, 10, 100, 1000)
	 */
	public function get_path_by_id($id)
	{
		$tree = array();
		
		if ($id != 0) {
			do {
				$item = $this->select_instance_by_id($id);
				if (empty($item)) break;
				if (in_array($item['id'], $tree)) break;//避免脏数据导致死循环
				
				$tree[] = $item['id'];
				$id = $item['parent_id'];
			} while ($id > 0);
			
			$tree = array_reverse($tree);
		}
		
		array_unshift($tree, 0);
		
		return $tree;
	}
	
	/**
	 * 返回给定分类ID的各级路径，正向分类树
	 * 
	 * @param string $position 给定分类的$position
	 * 
	 * @return array 如果分类不存在返回零数组[0]，如果存在返回从顶级到本类的id路径
	 */
	public function calculate_path_by_position($position)
	{
		$prefix = 'tr_';
		
		$path = explode(" {$prefix}", trim($position, " \t\n\r\0\x0B|{$prefix}"));
		
		return $path;
	}
	
	/**
	 * 根据 $path 计算 位置路径 position
	 * 
	 * @desc  ($path 就是函数 get_path_by_id 的返回值形式)
	 * 
	 * @param array $path
	 * 
	 * @return string 从顶级到本类的位置路径字符串
	 *                eg: 0    -> tr_0
	 *                    1    -> tr_0 tr_1
	 *                    10   -> tr_0 tr_1 tr_10
	 *                    1000 -> tr_0 tr_1 tr_10 tr_100 tr_1000
	 */
	public function calculate_position_by_path($path)
	{
		$prefix = 'tr_';
		array_walk($path, function(&$item, $key, $prefix){
			$item = "{$prefix}{$item}";
		}, $prefix);
		
		$position = implode(' ', $path);
		
		return $position;
	}
	
	/**
	 * 获取 position: 给定父级ID的各级路径，正向分类树
	 * 
	 * @param int $id 给定分类ID
	 * 
	 * @return string 如果分类不存在返回起始位置 tr_0，如果存在返回从顶级到本类的位置路径
	 *                eg: 0    -> tr_0
	 *                    1    -> tr_0 tr_1
	 *                    10   -> tr_0 tr_1 tr_10
	 *                    1000 -> tr_0 tr_1 tr_10 tr_100 tr_1000
	 */
	public function get_position_by_parent_id($parent_id)
	{
		$path = $this->get_path_by_id($parent_id);
		
		$position = $this->calculate_position_by_path($path);
		
		return $position;
	}
	
	
	/**
	 * 根据位置路径 position 获得叶子节点的全树数据
	 * 
	 * @param string  $position 数据库字段
	 * 
	 * @return array 非0的数字索引代表 在当前Tree中存在的分类
	 */
	public function get_leaf_data_by_position($position)
	{
		$siblings = $this->get_tree_data_by_position($position);
		
		$path = explode(' ', $position);
		$segment = array_pop($path);
		$id = str_replace('tr_', '', $segment);
		$leafs = $this->get_children_by_id($id);
		
		$siblings[0] = $leafs;
		
		return $siblings;
	}
	
	/**
	 * 根据位置路径 position 获取地区的数据信息
	 * 
	 * @param string  $position 数据库字段
	 * 
	 * @return array 非0的数字索引代表 在当前Tree中存在的分类
	 * 				非0数字索引代表路径中的各个节点（所代表的地区ID）：当前选中的路径节点 => 当前节点所在级别的地区组
	 */
	public function get_tree_data_by_position($position)
	{
		$treeData = array();
		
		$path = explode(' ', $position);
		if(empty($path)) return $treeData;
		
		foreach ($path as $segment) {
			$id = str_replace('tr_', '', $segment);
			$siblings = $this->get_siblings_by_id($id);
			
			if (is_array($siblings)) {
				$treeData[$id] = $siblings;
			}
		}
		
		return $treeData;
	}
	
	
	/**
	 * 插入前处理器
	 * 
	 * @desc	如果派生类需要重定义该方法，需要主动手动调用！！！先后无所谓。
	 * 
	 * @param array  $data
	 * @param string $mapping	表映射
	 * 
	 * @return array
	 * 
	 * @version	1.1.0
	 * @desc	level from 0
	 * @author	Jason
	 * @date	2017年7月5日 上午11:12:20
	 */
	public function before_treeStructure_create_data_handler($data, $mapping = 'master')
	{
		if ($mapping === 'master') {
			$data['position'] = $this->get_position_by_parent_id($data['parent_id']);
			$data['level'] = count(explode(' ', $data['position'])) - 1; // from 0
		}
		
		return $data;
	}
	
}